home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d20 / msgq160s.arc / QMSGBASE.C < prev    next >
Text File  |  1991-10-26  |  25KB  |  1,040 lines

  1. /*
  2.  * QMSGBASE.C - Low level message base functions
  3.  *
  4.  * Msged/Q message editor for QuickBBS  Copyright 1990 by P.J. Muller
  5.  *
  6.  */
  7.  
  8. /*
  9.  * Used by Msged/Q and MsgLink
  10.  *
  11.  * External variables:
  12.  *   char *bbspath    Prefix for *.BBS filenames
  13.  *   int usernum    For LASTREAD.BBS, set this to 0 if not needed
  14.  *
  15.  * Conditional Compilation:
  16.  *   NOBINSEARCH    Define this for slow linear indexing
  17.  *   OLDBINIDX
  18.  */
  19.  
  20. #include <string.h>
  21. #include <io.h>
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <fcntl.h>
  25. #include <time.h>
  26. #include <sys/stat.h>
  27.  
  28. #include "qtypes.h"
  29. #include "quickbbs.h"
  30. #include "qmsgbase.h"
  31. #include "p2c.h"
  32.  
  33. #define PATHLEN 70
  34.  
  35. extern char *bbspath;        /* Path prefix for filenames */
  36. extern int usernum;        /* user number for lastread.bbs */
  37.  
  38. static int lastread[BLIM];    /* last msg read in each board */
  39. static int current[BLIM];    /* current msgnum in each board */
  40. static BOOLEAN lastreaddirty = FALSE;
  41. static BOOLEAN currentdirty = FALSE;
  42. static INFORECORD msginfo;    /* MSGINFO.BBS */
  43. IDXRECORD *msgidx;        /* MSGIDX.BBS */
  44. int filemsgs;            /* length of index and header files */
  45.  
  46. /* file descriptors indexes, be careful when changing this (for loops) */
  47.  
  48. enum {f_idx,f_info,f_hdr,f_txt,f_toidx,f_cur,f_last};
  49.  
  50. static int F[f_last+1];
  51. static char *FN[]={"msgidx.bbs","msginfo.bbs","msghdr.bbs","msgtxt.bbs",
  52.         "msgtoidx.bbs","current.bbs","lastread.bbs"};
  53.  
  54. #define f_first f_idx        /* loops from f_first to f_last */
  55.  
  56. /* functions local to this module */
  57.  
  58. static BOOLEAN updatecurlast(void);
  59. static BOOLEAN flushfile(int fd);
  60. static BOOLEAN updatetosslog(int num, BOOLEAN echomail, BOOLEAN delete);
  61.  
  62. /*
  63.  * Return a pointer to a static expanded path
  64.  */
  65.  
  66. char *expandbbs(char *fname)
  67. {
  68.   static char name[PATHLEN];
  69.  
  70.   name[0] = EOS;
  71.   if (bbspath != NULL)
  72.     strcpy(name, bbspath);
  73.   strcat(name, fname);
  74.   return(name);
  75. } /* expandbbs */
  76.  
  77. /*
  78.  * Open all the message base files
  79.  * Returns TRUE if ok
  80.  */
  81.  
  82. BOOLEAN openmsgbase()
  83. {
  84.   struct stat st;
  85.   BYTE i;
  86.  
  87.   for (i = f_first;  i <= f_last;  i++)        /* open all files */
  88.     if ((F[i] = open(expandbbs(FN[i]),
  89.     O_RDWR|O_BINARY|O_CREAT, S_IREAD|S_IWRITE)) == -1)
  90.       return(FALSE);
  91.  
  92.   memset(&msginfo, 0, sizeof(msginfo));        /* clear the record */
  93.   fstat(F[f_info], &st);
  94.   if (st.st_size == 0) {            /* just created */
  95.     write(F[f_info], (char *)&msginfo, sizeof msginfo);
  96.     flushfile(F[f_info]);
  97.   } else {
  98.     read(F[f_info], (char *)&msginfo, sizeof msginfo);    /* read existing */
  99.   } /* if */
  100.  
  101.   memset(&st, 0, sizeof(st));
  102.   fstat(F[f_hdr], &st);
  103.  
  104.   filemsgs = (int)(st.st_size/sizeof(MSGHEADER));    /* number of messages */
  105.   if ((msgidx = calloc(filemsgs+1,sizeof(IDXRECORD))) == NULL)
  106.     return FALSE;
  107.   read(F[f_idx], (char *)msgidx, (size_t)filemsgs*sizeof(IDXRECORD));
  108.  
  109.   memset(lastread, 0, sizeof(lastread));
  110.   lseek(F[f_last], (long)usernum * sizeof(lastread), SEEK_SET);
  111.   read(F[f_last], (char *)lastread, sizeof(lastread));
  112.             /* pssst: could check for errors here */
  113.  
  114.   memset(current, 0, sizeof(current));
  115.   lseek(F[f_cur], (long)usernum * sizeof(lastread), SEEK_SET);
  116.   read(F[f_cur], (char *)current, sizeof(current));
  117.  
  118.   for (i = 0;  i < BLIM;  i++) {
  119.     int first,last;
  120.  
  121.     if (msginfo.activemsgs[i] == 0)    /* empty board? */
  122.       continue;
  123.  
  124.     first = firstmsg(i+1);
  125.     last  = lastmsg(i+1);
  126.  
  127.     if (lastread[i] > last)
  128.       lastread[i] = last;
  129.     else if ((lastread[i] < first) && (lastread[i] != 0))
  130.       lastread[i] = first;
  131.     else if (!msgat(i+1,lastread[i]))
  132.       lastread[i] = msgprev(i+1,lastread[i]);
  133.  
  134.     if (current[i] > lastread[i])
  135.       current[i] = lastread[i];
  136.     else if (current[i] < first)
  137.       current[i] = first;
  138.  
  139.   } /* for */
  140.  
  141.   return TRUE;
  142. } /* openmsgbase */
  143.  
  144. /*
  145.  * Return the number of messages in a board
  146.  */
  147.  
  148. #define COUNTMSG(board) (msginfo.activemsgs[(board)-1])    /* local version */
  149.  
  150. int countmsg(BYTE board)    /* for external calls */
  151. {
  152.   return COUNTMSG(board);
  153. } /* countmsg */
  154.  
  155. /*
  156.  * Update the current and lastread pointers to disk
  157.  */
  158.  
  159. static BOOLEAN updatecurlast(void)
  160. {
  161.   struct stat st;
  162.   int count;
  163.   BOOLEAN result = TRUE;
  164.  
  165.   if (currentdirty) {
  166.     BYTE b;
  167.  
  168.     for (b = 0;  b < BLIM;  b++)
  169.       if (COUNTMSG(b+1) == 0)
  170.     current[b] = 0;
  171.  
  172.     st.st_size = 0;
  173.     fstat(F[f_cur], &st);
  174.     count = (int)(st.st_size/sizeof(current));        /* current size of file */
  175.  
  176.     if (usernum <= count) {
  177.       if (lseek(F[f_cur], (long)usernum * sizeof(current), SEEK_SET) == -1)
  178.     result = FALSE;
  179.       if (write(F[f_cur], (char *)current, sizeof(current)) != sizeof(current))
  180.     result = FALSE;
  181.     } else {
  182.       while (count <= usernum) {
  183.     if (lseek(F[f_cur], (long)count * sizeof(current), SEEK_SET) == -1)
  184.       result = FALSE;
  185.     if (write(F[f_cur], (char *)current, sizeof(current)) != sizeof(current))
  186.       result = FALSE;
  187.         ++count;
  188.       } /* while */
  189.     } /* else */
  190.  
  191.   } /* if */
  192.  
  193.   if (lastreaddirty) {
  194.     BYTE b;
  195.  
  196.     for (b = 1;  b <= BLIM;  b++)
  197.       if (lastread[b-1] > lastmsg(b))
  198.     setlastread(b, lastmsg(b));
  199.  
  200.     if (lseek(F[f_last], (long)usernum * sizeof(lastread), SEEK_SET) == -1)
  201.       result = FALSE;
  202.     if (write(F[f_last], (char *)lastread, sizeof(lastread)) != sizeof(lastread))
  203.       result = FALSE;
  204.   } /* if */
  205.  
  206.   return(result);
  207. } /* updatecurlast */
  208.  
  209. /*
  210.  * Close all message base files
  211.  * Return TRUE if ok
  212.  */
  213.  
  214. BOOLEAN closemsgbase()
  215. {
  216.   BYTE i;
  217.   BOOLEAN result = TRUE;
  218.  
  219.   if (!updatecurlast())
  220.     result = FALSE;
  221.  
  222.   for (i = f_first;  i <= f_last;  i++)
  223.     if (F[i] != 0)
  224.       if (close(F[i]) == -1)
  225.     result = FALSE;
  226.  
  227.   free(msgidx);
  228.   msgidx = NULL;
  229.  
  230.   for (i = f_first;  i <= f_last;  i++)
  231.     F[i] = 0;
  232.  
  233.   return(result);
  234. } /* closemsgbase */
  235.  
  236. /*
  237.  * Update the directory entry of a file
  238.  */
  239.  
  240. static BOOLEAN flushfile(int fd)
  241. {
  242.   int new;
  243.  
  244.   if ((new = dup(fd)) == -1)    /* duplicate the file handle */
  245.     return(FALSE);
  246.   if (close(new) == -1)        /* and close the duplicate */
  247.     return(FALSE);
  248.  
  249.   return(TRUE);
  250. } /* flushfile */
  251.  
  252. /*
  253.  * Flush message base
  254.  * Make sure FAT is updated by closing and opening all message files
  255.  */
  256.  
  257. BOOLEAN flushmsgbase()
  258. {
  259.   BYTE i;
  260.   BOOLEAN result = TRUE;
  261.  
  262.   if (!updatecurlast())
  263.     result = FALSE;
  264.  
  265.   for (i = f_first;  i <= f_last;  i++)        /* flush all files */
  266.     if (!flushfile(F[i]))
  267.       result = FALSE;
  268.  
  269.   return(result);
  270. } /* flushmsgbase */
  271.  
  272. /*
  273.  * Return the current message of a board
  274.  */
  275.  
  276. int curmsg(BYTE board)
  277. {
  278.   return(current[board-1]);
  279. } /* curmsg */
  280.  
  281. /*
  282.  * Return the 'lastread' message of a board
  283.  */
  284.  
  285. int lastreadmsg(BYTE board)
  286. {
  287.   return(lastread[board-1]);
  288. } /* lastreadmsg */
  289.  
  290. /*
  291.  * Set the 'current' message of a board
  292.  */
  293.  
  294. void setcur(BYTE board, int msgnum)
  295. {
  296.   currentdirty = TRUE;
  297.   current[board-1] = msgnum;
  298. } /* setcur */
  299.  
  300. /*
  301.  * Set the 'lastread' message of a board
  302.  */
  303.  
  304. void setlastread(BYTE board, int msgnum)
  305. {
  306.   lastreaddirty = TRUE;
  307.   lastread[board-1] = msgnum;
  308. } /* setlastread */
  309.  
  310. /*
  311.  * Return the number of the message in the board
  312.  * Returns 0 if message not in board
  313.  */
  314.  
  315. int boardmsg(BYTE board, int cur)
  316. {
  317.   register IDXRECORD *step;
  318.   register int fcount;
  319.   int count = 0;
  320.  
  321.   for (step = msgidx, fcount = filemsgs;  fcount--;  step++)
  322.     if ((step->board == board) && (step->msgnum != -1)) {
  323.       ++count;
  324.       if (step->msgnum == cur)
  325.     return count;
  326.     } /* if */
  327.  
  328.   return 0;
  329. } /* boardmsg */
  330.  
  331. /*
  332.  * Return the number of first message found in the board
  333.  * Return 0 if the board is empty
  334.  */
  335.  
  336. int firstmsg(BYTE board)
  337. {
  338.   register IDXRECORD *step;
  339.   int count;
  340.  
  341.   if (COUNTMSG(board) == 0) return 0;
  342.  
  343.   for (step = msgidx, count = filemsgs;  count--;  step++)
  344.     if ((step->board == board) && (step->msgnum != -1))
  345.       return(step->msgnum);
  346.  
  347.   return 0;
  348. } /* firstmsg */
  349.  
  350. /*
  351.  * Return the number of the last message in the board
  352.  * Return 0 if the board is empty
  353.  */
  354.  
  355. int lastmsg(BYTE board)
  356. {
  357.   register IDXRECORD *step;
  358.   int count;
  359.  
  360.   if (COUNTMSG(board) == 0) return 0;
  361.  
  362.   for (step = &msgidx[filemsgs-1], count = filemsgs;  count--;  step--)
  363.     if ((step->board == board) && (step->msgnum != -1))
  364.       return(step->msgnum);
  365.  
  366.   return 0;
  367. } /* lastmsg */
  368.  
  369. /*
  370.  * Binary search in msgidx
  371.  * Handles deleted entries (out of sort) by skipping alternatingly
  372.  * left and right from the middle until a non-deleted entry is found.
  373.  * May become slower when there are a lot of deleted entries.
  374.  */
  375.  
  376. #ifndef NOBINSEARCH
  377. #ifdef OLDBINIDX
  378. static int binidx(int num, BOOLEAN *found)
  379. {
  380.   int mid, stab, skip, skipend;
  381.   register int left = 0;
  382.   register int right = (filemsgs-1);
  383.  
  384.   *found = TRUE;
  385.  
  386.   while (left <= right) {
  387.     skip = 1;
  388.     mid = (left+right)/2;
  389.  
  390.     skipend = (max(mid-left,right-mid))*2 + 1;
  391.     do {
  392.       if ((mid >= left) && (mid <= right))
  393.     if ((stab = msgidx[mid].msgnum) != -1)
  394.       break;            /* found non-deleted msg */
  395.       mid += skip;        /* do a sideways step */
  396.       skip = (skip < 0) ? (-skip)+1 : -(skip+1);  /* +1 -2 +3 -4 +5 -6 ... */
  397.     } while (abs(skip) <= skipend);
  398.     if (stab == -1) break;    /* return FALSE */
  399.  
  400.     if (num == stab)
  401.       return(mid);
  402.     if (num < stab)
  403.       right = mid-1;
  404.     else
  405.       left = mid+1;
  406.   } /* while */
  407.  
  408.   *found = FALSE;
  409.   return(left);
  410. } /* binidx */
  411. #else
  412. static int binidx(int num, BOOLEAN *found)
  413. {
  414. # define X ((unsigned int)num)
  415. # define A(i) ((unsigned int)msgidx[i].msgnum)
  416. # define M ((unsigned int)-1)
  417. # define N filemsgs
  418.   register unsigned int a,b;
  419.  
  420.   *found = FALSE;
  421.   if (N <= 0) return 0;
  422.  
  423.   a = 0;  b = N-1;
  424.   while (a != b) {
  425.     register unsigned int m = (a+b) / 2;
  426.  
  427.     if (A(m) == X)
  428.       a = b = m;
  429.     else if (A(m) < X)
  430.       a = m+1;
  431.     else if ((A(m) > X) && (A(m) != M))
  432.       b = m;
  433.     else if ((A(b) > X) && (A(m) == M))
  434.       --b;
  435.     else if ((A(b) <= X) && (A(m) == M))
  436.       a = b;
  437.   }
  438.   *found = (A(a) == X);
  439.   return a;
  440. }
  441. #undef X
  442. #undef A
  443. #undef M
  444. #undef N
  445.  
  446. #endif
  447. #endif
  448.  
  449. /*
  450.  * Return the number of the next message in the board
  451.  * Return last message if next message not found
  452.  */
  453.  
  454. int msgnext(BYTE board, int cur)
  455. {
  456.   int step;
  457.  
  458.   if (COUNTMSG(board) == 0) return 0;
  459.  
  460. #ifdef NOBINSEARCH
  461.   step = -1;
  462. #else
  463.   { BOOLEAN found;    /* not used */
  464.     step = binidx(cur,&found);
  465.   }
  466. #endif
  467.  
  468.   while (++step < filemsgs)        /* not full match, look further */
  469.     if (msgidx[step].board == board) /*&& (msgidx[step].msgnum != -1)*/
  470.       if (msgidx[step].msgnum > cur)
  471.     return(msgidx[step].msgnum);
  472.  
  473.   return(lastmsg(board));    /* no greater msg found, 'cur' must be last message */
  474. } /* msgnext */
  475.  
  476. /*
  477.  * Return the number of the previous message in the board
  478.  * Return first message if previous not found
  479.  */
  480.  
  481. int msgprev(BYTE board, int cur)
  482. {
  483.   int step;
  484.  
  485.   if (COUNTMSG(board) == 0) return 0;
  486.  
  487. #ifdef NOBINSEARCH
  488.   step = filemsgs;
  489. #else
  490.   { BOOLEAN found;    /* not used */
  491.     step = binidx(cur,&found);
  492.   }
  493. #endif
  494.  
  495.   while (--step >= 0)
  496.     if ((msgidx[step].board == board) && (msgidx[step].msgnum != -1))
  497.       if (msgidx[step].msgnum < cur)
  498.     return(msgidx[step].msgnum);
  499.  
  500.   return(firstmsg(board));        /* no smaller msg found, 'cur' must be first */
  501. } /* msgprev */
  502.  
  503. /*
  504.  * Return the file message number of a message (may be 0)
  505.  * Return -1 if not found
  506.  */
  507.  
  508. int filemsg(int msgnum)
  509. {
  510.   int step;
  511.  
  512.   if ((msgnum > msginfo.highmsg) || (msgnum < msginfo.lowmsg))
  513.     return(-1);
  514.  
  515. #ifdef NOBINSEARCH
  516.   for (step = 0;  step < filemsgs;  step++)
  517.     if (msgidx[step].msgnum == msgnum)
  518.       return(step);
  519. #else
  520.   { BOOLEAN found;
  521.     step = binidx(msgnum,&found);
  522.     if (found)
  523.       return(step);
  524.   }
  525. #endif
  526.  
  527.   return(-1);
  528. } /* filemsg */
  529.  
  530. /*
  531.  * Return TRUE if there is a 'msgnum' in the Board
  532.  */
  533.  
  534. BOOLEAN msgat(BYTE board, int msgnum)
  535. {
  536.   int fmsg;
  537.  
  538.   if ((msgnum == -1) || ((fmsg = filemsg(msgnum)) == -1))
  539.     return FALSE;
  540.  
  541.   return(msgidx[fmsg].board == board);
  542. } /* msgat */
  543.  
  544. /*
  545.  * Read a message header
  546.  * Return TRUE if ok
  547.  */
  548.  
  549. BOOLEAN readheader(int msgnum, MSGHEADER *m)
  550. {
  551.   int n;
  552.  
  553.   if ((n = filemsg(msgnum)) == -1)
  554.     return FALSE;
  555.  
  556.   if (lseek(F[f_hdr], (long)n * sizeof(MSGHEADER), SEEK_SET) == -1)
  557.     return FALSE;
  558.  
  559.   if (read(F[f_hdr], (char *)m, sizeof(MSGHEADER)) != sizeof(MSGHEADER))
  560.     return FALSE;
  561.  
  562.   p2c_strn(m->posttime,5);        /* pascal to c strings */
  563.   p2c_strn(m->postdate,8);
  564.   p2c_strn(m->to,35);
  565.   p2c_strn(m->from,35);
  566.   p2c_strn(m->subj,72);
  567.  
  568.   return TRUE;
  569.  
  570. } /* readheader */
  571.  
  572. /*
  573.  * Write the header of a message
  574.  * Message number must be in index file in memory
  575.  */
  576.  
  577. BOOLEAN writeheader(MSGHEADER *hdr)
  578. {
  579.   MSGHEADER m;            /* local copy */
  580.   int fmsg;
  581.  
  582.   m = *hdr;            /* make a local copy */
  583.  
  584.   if ((fmsg = filemsg(m.msgnum)) == -1) return FALSE;
  585.  
  586.   c2p_str(m.posttime);
  587.   c2p_str(m.postdate);
  588.   c2p_str(m.to);
  589.   c2p_str(m.from);
  590.   c2p_str(m.subj);
  591.  
  592.   if (lseek(F[f_hdr], (long)fmsg * sizeof(MSGHEADER),SEEK_SET) == -1) return FALSE;
  593.   if (write(F[f_hdr], (char *)&m, sizeof(MSGHEADER)) != sizeof(MSGHEADER)) return FALSE;
  594.  
  595.   return TRUE;
  596. } /* writeheader */
  597.  
  598. /*
  599.  * Allocate and read the text of a message
  600.  * Return NULL if not ok
  601.  * MSGLINK.C knows a lot about this function, e.g. which fields of
  602.  *   m it doesn't access...
  603.  */
  604.  
  605. char *readtext(MSGHEADER *m)
  606. {
  607.   char *text, *step;
  608.   int count;
  609.  
  610.   if (m->numrecs == 0)            /* is empty message */
  611.     return(calloc(1,1));        /* empty string or NULL */
  612.  
  613.   if (lseek(F[f_txt], (long)m->startrec * 256, SEEK_SET) == -1)
  614.     return NULL;
  615.  
  616.   if ((text = calloc(m->numrecs,256)) == NULL)
  617.     return NULL;
  618.  
  619.   step = text;                /* now read the text */
  620.   for (count = 0;  count < m->numrecs;  count++) {
  621.     /* working(YES); */
  622.     if (read(F[f_txt], step, 256) != 256) {
  623.       free(text);
  624.       return NULL;
  625.     } /* if */
  626.     p2c_str(step);            /* pascal to c string */
  627.     step += strlen(step);        /* move step to EOS */
  628.   } /* for */
  629.  
  630.   /* working(NO); */
  631.   return(realloc(text, strlen(text)+1));    /* return text or NULL */
  632. } /* readtext */
  633.  
  634. /*
  635.  * Increment message counters in msginfo.bbs
  636.  * Return TRUE if ok
  637.  */
  638.  
  639. BOOLEAN incmsginfo(BYTE board)
  640. {
  641.  
  642.   msginfo.highmsg++;
  643.   msginfo.totalactive++;
  644.   msginfo.activemsgs[board-1]++;
  645.  
  646.   if (lseek(F[f_info],0L,SEEK_SET) == -1)
  647.     return FALSE;
  648.   if (write(F[f_info], (char *)&msginfo, sizeof(msginfo)) != sizeof(msginfo))
  649.     return FALSE;
  650.  
  651.   return TRUE;
  652.  
  653. } /* incmsginfo */
  654.  
  655. /*
  656.  * Return the next available message number for message creation
  657.  */
  658.  
  659. int newmsgnum()
  660. {
  661.   return(msginfo.highmsg+1);
  662. } /* newmsgnum */
  663.  
  664. /*
  665.  * Set one record of msgidx.bbs
  666.  * Return TRUE if ok
  667.  */
  668.  
  669. BOOLEAN writemsgidx(int fmsg, int msgnum, BYTE board)
  670. {
  671.  
  672.   if (fmsg >= filemsgs) {
  673.     filemsgs = fmsg+1;
  674.     if ((msgidx = realloc(msgidx, filemsgs*sizeof(IDXRECORD))) == NULL)
  675.       return(FALSE);        /* pssst: this is a DISASTER! */
  676.   } /* if */
  677.  
  678.   msgidx[fmsg].msgnum = msgnum;
  679.   msgidx[fmsg].board = board;
  680.  
  681.   if (lseek(F[f_idx],(long)fmsg * sizeof(IDXRECORD),SEEK_SET) == -1)
  682.     return FALSE;
  683.   if (write(F[f_idx], (char *)&msgidx[fmsg], sizeof(IDXRECORD)) != sizeof(IDXRECORD))
  684.     return FALSE;
  685.  
  686.   return TRUE;
  687.  
  688. } /* writemsgidx */
  689.  
  690. /*
  691.  * Write the msgtxt.bbs and msghdr.bbs files
  692.  * Return TRUE if ok
  693.  */
  694.  
  695. BOOLEAN writemsgtxthdr(MSGHEADER *hdr,  BOOLEAN oldmsg, char *textbuf)
  696. {
  697.   MSGHEADER oldhdr;
  698.   int needrecs;            /* number of records needed */
  699.   long pos;
  700.   int c1, ch;
  701.   static char wstr[256];
  702.  
  703.   needrecs = ((strlen(textbuf)+254) / 255);
  704.  
  705.   if (oldmsg) {            /* get old header */
  706.     if (!readheader(hdr->msgnum, &oldhdr))
  707.       return FALSE;
  708.   } else oldhdr.numrecs = 0;
  709.  
  710.   if ((!oldmsg) || (needrecs > oldhdr.numrecs)) {    /* need more records */
  711.     if ((pos = lseek(F[f_txt],0L,SEEK_END)) == -1) return FALSE;
  712.     hdr->startrec = (int)(pos/256);
  713.   } else {                /* overwrite old text */
  714.     if (lseek(F[f_txt], (long)oldhdr.startrec * 256,SEEK_SET) == -1) return FALSE;
  715.     hdr->startrec = oldhdr.startrec;
  716.   } /* else */
  717.  
  718.   hdr->numrecs = 0;
  719.   c1 = 0;                    /* now write the text */
  720.   memset (wstr, 0, sizeof(wstr));
  721.   for (;;) {
  722.     ch = *textbuf++;
  723.     if ((c1 == 255) || (ch == EOS)) {
  724.       wstr[0] = c1;            /* terminate current string */
  725.       if (write(F[f_txt], wstr, sizeof(wstr)) != sizeof(wstr)) return FALSE;
  726.       hdr->numrecs++;            /* next record number */
  727.       if (ch == 0)
  728.     break;                /* out of forever */
  729.       else {
  730.     memset(wstr, 0, sizeof(wstr));
  731.     c1 = 0;
  732.       } /* else */
  733.     } /* if */
  734.     wstr[++c1] = ch;
  735.   }  /* forever */
  736.  
  737.   if (!writeheader(hdr)) return FALSE;
  738.  
  739.   return TRUE;
  740.  
  741. } /* writemsgtxthdr */
  742.  
  743. /*
  744.  * Overwrite or add to msgtoidx.bbs
  745.  * Return TRUE if ok
  746.  */
  747.  
  748. BOOLEAN writetoidx(int fmsg, char *to)
  749. {
  750.   static char buf[36];
  751.  
  752.   memset(buf,0,sizeof(buf));
  753.   strncpy(buf,to,35);
  754.   c2p_str(buf);
  755.  
  756.   if (lseek(F[f_toidx], (long)fmsg * 36, SEEK_SET) == -1) return FALSE;
  757.   if (write(F[f_toidx], buf,sizeof(buf)) != sizeof(buf)) return FALSE;
  758.  
  759.   return TRUE;
  760. } /* writetoidx */
  761.  
  762. /*
  763.  * Read from MSGTOIDX.BBS
  764.  * Return pointer to static area or NULL if error
  765.  */
  766.  
  767. char *readtoidx(int fmsg)
  768. {
  769.   static char buf[36];
  770.  
  771.   if (lseek(F[f_toidx], (long)fmsg * 36, SEEK_SET) == -1) return NULL;
  772.   if (read(F[f_toidx], &buf[0], sizeof(buf)) != sizeof(buf)) return NULL;
  773.  
  774.   p2c_strn(buf,35);
  775.   return(buf);
  776. } /* readtoidx */
  777.  
  778. /*
  779.  * Write ECHOMAIL.BBS or NETMAIL.BBS
  780.  * Return TRUE if ok
  781.  */
  782.  
  783. static BOOLEAN updatetosslog(int num, BOOLEAN echomail, BOOLEAN delete)
  784. {
  785.   int fd, pos, *tosslog;
  786.   struct stat st;
  787.   char *name = (echomail ? "echomail.bbs" : "netmail.bbs");
  788.  
  789.   if ((fd = open(expandbbs(name), O_RDWR|O_BINARY|O_CREAT,
  790.         S_IREAD|S_IWRITE)) == -1)
  791.     return FALSE;
  792.  
  793.   fstat(fd, &st);    /* get size */
  794.   tosslog = (int *)malloc((int)st.st_size+2);    /* add space for insert */
  795.  
  796.   if ((tosslog != NULL) && (st.st_size != 0)) {
  797.     read(fd,(char *)tosslog, (int)st.st_size);
  798.     for (pos = (int)(st.st_size/2-1);  pos >= 0;  pos--)
  799.       if (tosslog[pos] == num) break;
  800.   } else pos = -1;
  801.  
  802.   if (delete) {            /* remove a number */
  803.     if (pos >= 0) {        /* was found */
  804.       if (st.st_size-pos*2 > 2)
  805.     memmove(&tosslog[pos], &tosslog[pos+1], st.st_size-pos*2-2);  /* delete */
  806.       lseek(fd,0L,SEEK_SET);    /* rewind */
  807.       st.st_size -= 2;        /* shorten it */
  808.       write(fd,(char *)tosslog,st.st_size);    /* all but last */
  809.       chsize(fd,st.st_size);            /* change file size */
  810.     } /* if */
  811.   } else {            /* add a number */
  812.     if (pos < 0) {        /* was not found (not serious) */
  813.       int len = st.st_size/2;    /* number of elements */
  814.       pos = 0;
  815.       while ((pos < len) && (tosslog[pos] < num))    /* search */
  816.     pos++;
  817.       if (pos < len)        /* make space */
  818.     memmove(&tosslog[pos+1], &tosslog[pos], (len-pos)*2);
  819.       tosslog[pos] = num;    /* enter new */
  820.       st.st_size += 2;        /* add to length */
  821.       lseek(fd,0L,SEEK_SET);    /* rewind */
  822.       write(fd,(char *)tosslog,st.st_size);    /* and write it */
  823.     } /* if */
  824.   } /* else */
  825.  
  826.   if (tosslog != NULL) free(tosslog);
  827.   if (close(fd) == -1) return FALSE;
  828.   if (st.st_size == 0)        /* if empty, remove it */
  829.     unlink(expandbbs(name));
  830.   return TRUE;
  831. } /* updatetosslog */
  832.  
  833. /*
  834.  * Delete the message (only on current area because of tosslog)
  835.  * Return TRUE if ok
  836.  */
  837.  
  838. BOOLEAN msgdelete(int msgnum, BOOLEAN echomail, BOOLEAN netmail)
  839. {
  840.   MSGHEADER hdr, to, from;
  841.   int fmsg;
  842.   BYTE board;
  843.   int nto = 0;
  844.   int nfrm = 0;
  845.  
  846.   if ((fmsg = filemsg(msgnum)) == -1)
  847.     return FALSE;        /* no such message! */
  848.  
  849.   board = msgidx[fmsg].board;    /* board number of the message */
  850.  
  851. /* First set the deleted bit in MSGHDR.BBS */
  852.  
  853.   if (!readheader(msgnum, &hdr))
  854.     return FALSE;
  855.   if (hdr.bits.is_del)
  856.     return TRUE;        /* strange... but true? */
  857.  
  858.   hdr.bits.is_del = 1;        /* mark it deleted */
  859.  
  860.   if (!writeheader(&hdr))
  861.     return FALSE;
  862.  
  863. /* Now modify the reply links */
  864.  
  865.   nto = hdr.reply;        /* 'to' means link 'to' this header */
  866.   nfrm = hdr.up;        /* 'from' means link 'from' this header */
  867.  
  868.   if (nto != 0)
  869.     if (!readheader(nto, &to))
  870.       nto = 0;
  871.  
  872.   if (nfrm != 0)
  873.     if (!readheader(nfrm, &from))
  874.       nfrm = 0;
  875.  
  876.   from.reply = nto;
  877.   to.up = nfrm;
  878.  
  879.   if (nto != 0)            /* write the headers */
  880.     writeheader(&to);        /* ignore errors */
  881.  
  882.   if (nfrm != 0)
  883.     writeheader(&from);
  884.  
  885. /* Update MSGIDX.BBS */
  886.  
  887.   if (!writemsgidx(fmsg, -1, 0))
  888.     return FALSE;
  889.  
  890. /* Update MSGINFO.BBS */
  891.  
  892.   msginfo.totalactive--;
  893.   msginfo.activemsgs[board-1]--;
  894.   if (msgnum == msginfo.lowmsg)        /* was the low message */
  895.     msginfo.lowmsg++;            /* so increment it */
  896. /* I am not convinced that this is correct...  What happens if the new
  897.    'lowmsg' doesn't exist?  Ahh well... no documentation... ho hum. */
  898.  
  899.   if (lseek(F[f_info],0L,SEEK_SET) == -1)
  900.     return FALSE;
  901.   if (write(F[f_info], (char *)&msginfo, sizeof(msginfo)) != sizeof(msginfo))
  902.     return FALSE;
  903.  
  904. /* Update MSGTOIDX.BBS */
  905.  
  906.   if (!writetoidx(fmsg, "* Deleted *"))
  907.     return FALSE;
  908.  
  909.   if (echomail) {
  910.     if (!updatetosslog(fmsg,TRUE,TRUE))
  911.       return FALSE;
  912.   } else if (netmail) {
  913.     if (!updatetosslog(fmsg,FALSE,TRUE))
  914.       return FALSE;
  915.   } /* else */
  916.  
  917.   return TRUE;            /* all done */
  918.  
  919. } /* msgdelete */
  920.  
  921. /*
  922.  * Write a message into the message base
  923.  * This is about the highest level function provided
  924.  */
  925.  
  926. BOOLEAN writebase(MSGHEADER *header, BYTE board, char *textbuf,
  927.           BOOLEAN echomail, BOOLEAN netmail)
  928. {
  929.   int fmsg;
  930.   BOOLEAN oldmsg;
  931.  
  932.   fmsg = filemsg(header->msgnum);
  933.   oldmsg = (fmsg != -1);
  934.   if (!oldmsg)
  935.     fmsg = filemsgs;            /* next message */
  936.  
  937.   if (!writetoidx(fmsg, header->to))
  938.     return(FALSE);
  939.   if (!oldmsg)
  940.     if (!incmsginfo(board))
  941.       return(FALSE);
  942.   if (!writemsgidx(fmsg, header->msgnum, board))
  943.     return(FALSE);
  944.   if (!writemsgtxthdr(header, oldmsg, textbuf))
  945.     return(FALSE);
  946.  
  947.   if (echomail) {
  948.     if (!updatetosslog(fmsg,TRUE,FALSE))
  949.       return(FALSE);
  950.   } else if (netmail) {
  951.     if (!updatetosslog(fmsg,FALSE,FALSE))
  952.       return(FALSE);
  953.   } /* else */
  954.  
  955.   return(TRUE);
  956. } /* writebase */
  957.  
  958. /*
  959.  * Convert the time and date to Dos 32-bit format
  960.  */
  961.  
  962. DWORD dostime(char *t, char *d, BOOLEAN fudge)
  963. {
  964.   int day, mon, year, hour, min;
  965.   struct ftime dtime;
  966.  
  967.   sscanf(t, "%d:%d", &hour, &min);
  968.   sscanf(d, "%d-%d-%d", &mon, &day, &year);
  969.  
  970.   if (fudge)            /* a little more variation */
  971.     dtime.ft_tsec = ((clock()/(int)CLK_TCK)/2) % 30;
  972.   else
  973.     dtime.ft_tsec  = 0;
  974.   dtime.ft_min   = min;
  975.   dtime.ft_hour  = hour;
  976.   dtime.ft_day   = day;
  977.   dtime.ft_month = mon;
  978.   dtime.ft_year  = year-80;
  979.  
  980.   return(*((DWORD*)((void *)&dtime)));
  981. } /* dostime */
  982.  
  983. /*
  984.  * Check the message base files
  985.  * Call immediately after openmsgbase()
  986.  * Returns (in result and var parameter board):
  987.  *   CHK_NOERR    - no errors found
  988.  *   CHK_NONINC    - message numbers non-increasing in board (MSGIDX.BBS)
  989.  *   CHK_COUNTS    - message counts in MSGINFO.BBS different from MSGIDX.BBS
  990.  *   CHK_HDR    - length of MSGHDR.BBS different from MSGIDX.BBS
  991.  *   CHK_TO    - length of MSGTOIDX.BBS different from MSGIDX.BBS
  992.  */
  993.  
  994. int checkmsgbase(BYTE *boardresult)
  995. {
  996.   WORD board[BLIM], msgs[BLIM];
  997.   IDXRECORD *idx;
  998.   WORD count;
  999.   struct stat st;
  1000.  
  1001.   *boardresult = 0;
  1002.  
  1003.   memset(&board[0],0,sizeof board);    /* last number in board */
  1004.   memset(&msgs[0],0,sizeof msgs);    /* number of messages */
  1005.   for (idx = msgidx, count = 0;  count < filemsgs;  ++idx, ++count) {
  1006.     if (idx->msgnum == -1)
  1007.       continue;                /* skip deleted messages */
  1008.  
  1009.     if (idx->msgnum <= board[idx->board-1]) {
  1010.       /* non-increasing number found in board */
  1011.       *boardresult = idx->board;        /* return board number */
  1012.       return CHK_NONINC;            /* non-increasing */
  1013.     } /* if */
  1014.  
  1015.     board[idx->board-1] = idx->msgnum;        /* last num seen for board */
  1016.     ++msgs[idx->board-1];            /* another message in board */
  1017.   } /* for */
  1018.  
  1019.   for (count = 0;  count < BLIM;  ++count)
  1020.     if (msgs[count] != msginfo.activemsgs[count]) {
  1021.       /* count of messages is wrong */
  1022.       *boardresult = count+1;
  1023.       return CHK_COUNTS;            /* count wrong */
  1024.     } /* if */
  1025.  
  1026.   if (fstat(F[f_hdr],&st) != 0)
  1027.     st.st_size = 0;
  1028.   count = st.st_size/sizeof(MSGHEADER);        /* records in MSGHDR.BBS */
  1029.   if (count != filemsgs)
  1030.     return CHK_HDR;                /* not same as MSGIDX.BBS */
  1031.  
  1032.   if (fstat(F[f_toidx],&st) != 0)
  1033.     st.st_size = 0;
  1034.   count = st.st_size/36;            /* records in MSGTOIDX.BBS */
  1035.   if (count != filemsgs)
  1036.     return CHK_TO;                /* not same as MSGIDX.BBS */
  1037.  
  1038.   return CHK_NOERR;                /* no errors found */
  1039. } /* checkmsgbase */
  1040.